import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
import { fetchEventSource } from '@microsoft/fetch-event-source';
import {
  FetchEventSourceOptions,
  FetchEventSourceReturn,
  RequestParams,
  ErrorReportParam
} from '../types/aimeType';

let copyToClipboard: (textParams: string) => void = function (textParams: string): void {
  const execCopy = (text: string): void => {
    const textarea = document.createElement('textarea');
    textarea.value = text;
    document.body.appendChild(textarea);
    textarea.select();
    document.execCommand('copy');
    document.body.removeChild(textarea);
  };
  // 惰性函数，防止多次判断是否存在navigator.clipboard
  if (navigator.clipboard) {
    copyToClipboard = (text: string): void => {
      navigator.clipboard.writeText(text).catch(() => {
        execCopy(text);
      });
    };
  } else {
    copyToClipboard = execCopy;
  }
  copyToClipboard(textParams);
};

/**
 * 发送sse请求
 * @param options 请求配置
 * @returns 控制器 controller 请求状态 promise
 */
function sendSSE(options: FetchEventSourceOptions): {
  controller: AbortController;
  request: Promise<void>;
} {
  // 控制器 用于后续中断请求
  const controller = new AbortController();
  // 合并请求头
  const headers = {
    'Content-Type': 'application/json'
  };
  Object.assign(headers, options.headers);

  // 请求参数处理
  let body = '{}';
  if (options.data) {
    // get请求则参数拼接到url上
    if (options.method === 'GET') {
      const params = new URLSearchParams(options.data);
      options.url += '?' + params.toString();
    }
    // post请求则参数放在body中
    else {
      body = JSON.stringify(options.data);
    }
  }

  // 封装成promise 用于加载状态 及回答内容显隐控制
  const request = new Promise<any>(resolve => {
    fetchEventSource(options.url, {
      method: options.method || 'POST',
      headers,
      body,
      signal: controller.signal,
      openWhenHidden: true,
      credentials: options.credentials || 'include',
      onclose() {
        options.onclose && options.onclose();
        resolve(1);
      },
      onerror(err) {
        options.onerror && options.onerror(err);
        resolve(0);
      },
      onmessage(ev) {
        // 空行数据
        if (!ev.id && !ev.data && !ev.event) {
          return;
        }
        try {
          const formatData: FetchEventSourceReturn = {
            ...JSON.parse(JSON.stringify(ev))
          };
          options.onmessage && options.onmessage(formatData);
        } catch (error) {
          errorReport({
            name: `onmessage中JSON.parse错误${(error as Error)?.name}`,
            message: (error as Error)?.message || '',
            stack: (error as Error)?.stack || ''
          });
        }
      },
      onopen(response) {
        return new Promise(resolveFn => {
          options.onopen && options.onopen(response);
          resolveFn();
        });
      }
    });
  });
  return {
    controller,
    request
  };
}

const customFetch = ({
  url,
  method = 'GET',
  credentials = 'include',
  data = null,
  headers = {}
}: RequestParams) => {
  const options: RequestInit = {
    method,
    credentials,
    headers: {
      'Content-Type': 'application/json',
      ...headers
    }
  };

  if (data) {
    // @ts-ignore
    options.body = JSON.stringify(data);
  }

  return fetch(url, options)
    .then(response => {
      const res = response.json().then(res => {
        if (res.status_code) {
          throw new Error(
            `接口url：${url}的status_code为${res.status_code}；他的options是${JSON.stringify(
              options
            )}；他的响应值为${JSON.stringify(res)}`
          );
        }
        return res;
      });
      return res;
    })
    .catch(error => {
      console.error('Fetch error:', error);
      throw error;
    });
};

// 错误上报
let errorReport = (params: ErrorReportParam) => {
  if (window.ClientMonitor) {
    errorReport = (param: ErrorReportParam) => {
      const { name, message, stack, category = 'js' } = param;
      window.ClientMonitor.reportFrameErrors(
        {
          // 类型
          category: category,
          // 级别
          grade: 'Error'
        },
        {
          name,
          message,
          stack
        }
      );
    };
  } else {
    errorReport = () => {
      // 空函数，保持格式不变
    };
  }
  errorReport(params);
};

function getCookieValue(name: string): string | null {
  const cookies = document.cookie.split(';');
  for (const cookie of cookies) {
    const [key, value] = cookie.split('=');
    if (key.trim() === name) {
      return value ? value.trim() : null;
    }
  }
  return null;
}

function getUserId(): Promise<string | void> {
  return new Promise<string | void>((resolve, reject) => {
    if (!window.API) {
      resolve(getCookieValue('userid') + '');
    }
    window.API.use({
      method: 'Passport.get',
      data: 'userid',
      success: function (num: number) {
        resolve(num + '');
      },
      notClient: () => {
        resolve(getCookieValue('userid') + '');
      },
      error: (error: Error) => {
        resolve(getCookieValue('userid') + '');
      }
    });
  }).catch(error => {
    errorReport({
      name: '打开客户端弹框失败',
      message: (error as Error)?.message || '',
      stack: (error as Error)?.stack || ''
    });
  });
}

function arrPick(
  arr: any[],
  keyString: string,
  callback?: (obj: Record<string, any>, i: number) => void
) {
  if (!Array.isArray(arr)) {
    return arr;
  }
  if (!arr.length) {
    return [];
  }
  const keys = keyString.split('|');
  return arr.map((item, index) => {
    const obj: Record<string, any> = {};
    keys.forEach(key => {
      obj[key] = item[key];
    });
    if (callback) {
      const callbackObj = callback(item, index);
      Object.assign(obj, callbackObj);
    }
    return obj;
  });
}

function groupBy<T>(array: T[], key: (item: T) => string): Record<string, T[]> {
  return array.reduce((result: Record<string, T[]>, currentItem: T) => {
    // 使用 key 函数获取分组的键
    const groupKey = key(currentItem);

    // 确保 result 对象中有对应键的数组
    if (!result[groupKey]) {
      result[groupKey] = [];
    }

    // 将当前项添加到对应分组的数组中
    result[groupKey].push(currentItem);

    return result;
  }, {} as Record<string, T[]>);
}

//防抖
function debounce(fn: any, delay: number) {
  let timer: any = null;
  return function (...argu: any) {
    const args = argu;
    // @ts-ignore
    const context = this;
    if (timer) {
      clearTimeout(timer);
      timer = setTimeout(function () {
        fn.apply(context, args);
      }, delay);
    } else {
      timer = setTimeout(function () {
        fn.apply(context, args);
      }, delay);
    }
  };
}

export {
  copyToClipboard,
  sendSSE,
  customFetch,
  getUserId,
  arrPick,
  groupBy,
  errorReport,
  debounce
};
